// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package modload

import (
	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/mvs"
	"cmd/go/internal/par"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"

	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

// capVersionSlice返回s，并将其上限缩减为其长度。
func capVersionSlice(s []module.Version) []module.Version {
	return s[:len(s):len(s)]
}

// 需求表示逻辑上不可变的根模块需求集。
type Requirements struct {
	// 剪枝是计算需求图的剪枝。
	// 
	// 如果未运行，则该图包含所有可传递的需求，无论
	// 所需模块是否支持修剪。
	// 
	// 如果剪枝，该图仅包括根模块、这些根模块的显式
	// 需求，以及仅
	// 不支持剪枝的根模块的传递性需求。
	// 
	// 如果是工作空间，则该图仅包括工作空间模块、工作空间模块的显式
	// 要求，以及
	// 不支持修剪的工作空间模块的传递性要求。
	pruning modPruning

	// rootModules是图的根模块集，已排序并限制为
	// 长度。对于
	// 给定的模块路径，它可能包含重复项，也可能包含多个版本。groph的根模块是工作区模式下的一组主
	// 模块，以及工作区模式外的主模块直接需求
	// 模块。
	rootModules    []module.Version
	maxRootVersion map[string]string

	// direct是一组模块路径，我们认为该模块为其提供了
	// 由主模块中的包或测试直接导入的包。
	// 
	// 这个“直接”映射控制哪些模块在go.mod文件中被注释为“
	// 注释，并且可能会影响哪些模块被列为
	// 显式根（与仅间接依赖项相比）但是，它不应该对构建列表产生语义影响。
	// 
	// 初始直接映射由现有的“
	// 注释填充（或缺少注释）在路上。mod文件。它由
	// 程序包加载器更新：如果观察到新的
	// 直接导入，依赖项可能会升级为直接，并且在
	// “go mod tidy”或“go mod vendor”期间可能会降级为间接。
	// 
	// 直接映射由模块路径设置键，而不是模块版本。当
	// 模块选择的版本发生更改时，如果
	// 之前的版本是直接依赖的，我们假设它仍然是直接的。这种假设在
	// 罕见的情况下可能不成立（例如，如果依赖项拆分出一个嵌套模块，或者将一个
	// 嵌套模块合并回父模块）。
	direct map[string]bool

	graphOnce sync.Once    // guards写入（但不从）图形
	graph     atomic.Value // cachedGraph 
}

// cachedGraph是非零*模块图形，以及加载该图形时发现的任何错误
// 。
type cachedGraph struct {
	mg  *ModuleGraph
	err error // 如果err为非零，则mg可能不完整（但必须仍然为非零）。
}

// 需求是主模块的需求图。
// 
// 如果主模块运行，则始终为非零。mod文件已加载。
// 
// 该变量只能从loadModFile函数中读取，并且只能在loadModFile和CommitRequests函数中写入。
// 需要或产生*需求的所有其他函数应接受和/或返回显式参数。
var requirements *Requirements

// newRequirements返回一个包含给定根模块的新需求集。
// 在第一次调用
// Graph方法时，根的依赖项将被延迟加载。
// 
// rootModules切片必须根据模块进行排序。分类
// 在将rootModules slice或direct map传递给新需求后，调用方不得修改它们。
// 
// 如果vendoring有效，调用方必须在使用任何其他方法之前，对返回的
// /*要求调用initVendor。
func newRequirements(pruning modPruning, rootModules []module.Version, direct map[string]bool) *Requirements {
	if pruning == workspace {
		return &Requirements{
			pruning:        pruning,
			rootModules:    capVersionSlice(rootModules),
			maxRootVersion: nil,
			direct:         direct,
		}
	}

	if workFilePath != "" && pruning != workspace {
		panic("in workspace mode, but pruning is not workspace in newRequirements")
	}

	for i, m := range rootModules {
		if m.Version == "" && MainModules.Contains(m.Path) {
			panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is a main module", i))
		}
		if m.Path == "" || m.Version == "" {
			panic(fmt.Sprintf("bad requirement: rootModules[%v] = %v", i, m))
		}
		if i > 0 {
			prev := rootModules[i-1]
			if prev.Path > m.Path || (prev.Path == m.Path && semver.Compare(prev.Version, m.Version) > 0) {
				panic(fmt.Sprintf("newRequirements called with unsorted roots: %v", rootModules))
			}
		}
	}

	rs := &Requirements{
		pruning:        pruning,
		rootModules:    capVersionSlice(rootModules),
		maxRootVersion: make(map[string]string, len(rootModules)),
		direct:         direct,
	}

	for _, m := range rootModules {
		if v, ok := rs.maxRootVersion[m.Path]; ok && cmpVersion(v, m.Version) >= 0 {
			continue
		}
		rs.maxRootVersion[m.Path] = m.Version
	}
	return rs
}

// initVendor根据给定的供应商模块列表
// 依赖项初始化rs.graph，覆盖通常从模块
// 需求加载的图形。
func (rs *Requirements) initVendor(vendorList []module.Version) {
	rs.graphOnce.Do(func() {
		mg := &ModuleGraph{
			g: mvs.NewGraph(cmpVersion, MainModules.Versions()),
		}

		if MainModules.Len() != 1 {
			panic("There should be exactly one main module in Vendor mode.")
		}
		mainModule := MainModules.Versions()[0]

		if rs.pruning == pruned {
			// 经过修剪的模块的根应该已经包括了
			// 供应商列表中的每个模块，因为供应商提供的模块与进行图形修剪所需的
			// 模块相同。
			// 
			// 为了确定，我们会在这里再次检查。
			inconsistent := false
			for _, m := range vendorList {
				if v, ok := rs.rootSelected(m.Path); !ok || v != m.Version {
					base.Errorf("go: vendored module %v should be required explicitly in go.mod", m)
					inconsistent = true
				}
			}
			if inconsistent {
				base.Fatalf("go: %v", errGoModDirty)
			}

			// 现在我们可以有效地将模块图的其余部分视为“删减的
			// 输出”，就像我们从外部查看主模块一样：在供应商
			// 模式中，根需求*是*完整的模块图。
			mg.g.Require(mainModule, rs.rootModules)
		} else {
			// 主模块的可传递性需求通常无法从供应商目录中获得，我们也不知道如何从
			// 根目录获得最终构建列表。
			// 
			// 相反，我们将注入一个伪“vendor/modules.txt”模块，该模块提供
			// 这些可传递的依赖项，并将其标记为主
			// 模块的依赖项。这允许我们省略模块
			// 图的实际结构，但仍然区分直接依赖和间接依赖。
			vendorMod := module.Version{Path: "vendor/modules.txt", Version: ""}
			mg.g.Require(mainModule, append(rs.rootModules, vendorMod))
			mg.g.Require(vendorMod, vendorList)
		}

		rs.graph.Store(cachedGraph{mg, nil})
	})
}

// rootSelected返回具有给定模块
// path或零模块的根依赖项的版本。如果模块不是根
// 依赖项，则Version and ok=false。
func (rs *Requirements) rootSelected(path string) (version string, ok bool) {
	if MainModules.Contains(path) {
		return "", true
	}
	if v, ok := rs.maxRootVersion[path]; ok {
		return v, true
	}
	return "", false
}

// 如果根列表包含同一模块的多个需求
// 或主模块的任何版本上的一个需求，则hasRedundantRoot返回true。
// 应该删减冗余需求，但它们可能会影响版本
// 的选择。
func (rs *Requirements) hasRedundantRoot() bool {
	for i, m := range rs.rootModules {
		if MainModules.Contains(m.Path) || (i > 0 && m.Path == rs.rootModules[i-1].Path) {
			return true
		}
	}
	return false
}

// Graph返回从当前
// 根模块加载的模块需求图（由RootModules报告）。
// 
// Graph始终尽最大努力加载需求图，尽管存在任何
// 错误，并且始终返回非零*ModuleGraph。
// 
// 如果任何相关模块的需求未能加载，Graph也将返回*mvs类型的非零错误。BuildLister错误。
func (rs *Requirements) Graph(ctx context.Context) (*ModuleGraph, error) {
	rs.graphOnce.Do(func() {
		mg, mgErr := readModGraph(ctx, rs.pruning, rs.rootModules)
		rs.graph.Store(cachedGraph{mg, mgErr})
	})
	cached := rs.graph.Load().(cachedGraph)
	return cached.mg, cached.err
}

// IsDirect返回给定模块是否直接提供包
// 由主模块中的包或测试导入。
func (rs *Requirements) IsDirect(path string) bool {
	return rs.direct[path]
}

// ModuleGraph表示主模块的完整模块依赖关系图
// 。
// 
// 如果主模块支持模块图修剪，则该图不包括
// 非根（隐式）依赖的传递依赖。
type ModuleGraph struct {
	g         *mvs.Graph
	loadCache par.Cache // 模块。版本→ summaryError 

	buildListOnce sync.Once
	buildList     []module.Version
}

// summaryError是在读取或解析该摘要时遇到的非零modFileSummary或非零错误
// 错误。
type summaryError struct {
	summary *modFileSummary
	err     error
}

var readModGraphDebugOnce sync.Once

// readModGraph读取并返回从给定根开始的模块依赖关系图。与LoadModGraph不同，readModGraph不会尝试诊断或更新不一致的根。
func readModGraph(ctx context.Context, pruning modPruning, roots []module.Version) (*ModuleGraph, error) {
	if pruning == pruned {
		// 为惰性模块加载启用诊断
		// （https:
		// prunted.
		// 
		// 在未运行的模块中，我们更积极地加载模块图（在
		// 中，为了检测无法进行抽查的不一致性），
		// 因此，当发生这种情况时，记录日志是没有用的（因为它总是发生在
		// 正常操作中）。
		readModGraphDebugOnce.Do(func() {
			for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
				switch f {
				case "lazymod=log":
					debug.PrintStack()
					fmt.Fprintf(os.Stderr, "go: read full module graph.\n")
				case "lazymod=strict":
					debug.PrintStack()
					base.Fatalf("go: read full module graph (forbidden by GODEBUG=lazymod=strict).")
				}
			}
		})
	}

	var (
		mu       sync.Mutex // mg。加载
		hasError bool
		mg       = &ModuleGraph{
			g: mvs.NewGraph(cmpVersion, MainModules.Versions()),
		}
	)
	if pruning != workspace {
		if inWorkspaceMode() {
			panic("pruning is not workspace in workspace mode")
		}
		mg.g.Require(MainModules.mustGetSingleMainModule(), roots)
	}

	var (
		loadQueue       = par.NewQueue(runtime.GOMAXPROCS(0))
		loadingUnpruned sync.Map // 模块时，g和hasError出现错误。版本→ 无已经或正在通过根加载的一组模块不支持修剪
	)

	// loadOne同步加载模块m的明确需求。
	// 即使
	// m的go版本中的go，它也不会加载m的可传递需求。mod file表示它支持图形修剪。
	loadOne := func(m module.Version) (*modFileSummary, error) {
		cached := mg.loadCache.Do(m, func() any {
			summary, err := goModSummary(m)

			mu.Lock()
			if err == nil {
				mg.g.Require(m, summary.require)
			} else {
				hasError = true
			}
			mu.Unlock()

			return summaryError{summary, err}
		}).(summaryError)

		return cached.summary, cached.err
	}

	var enqueue func(m module.Version, pruning modPruning)
	enqueue = func(m module.Version, pruning modPruning) {
		if m.Version == "none" {
			return
		}

		if pruning == unpruned {
			if _, dup := loadingUnpruned.LoadOrStore(m, nil); dup {
				// m已排队等待加载。由于未运行的加载可能
				// 遵循需求图中的周期，我们需要返回早期
				// 以避免使加载队列无限长。
				return
			}
		}

		loadQueue.Add(func() {
			summary, err := loadOne(m)
			if err != nil {
				return // findError将在稍后报告错误。
			}

			// 如果m中的版本成功了。mod file不支持剪枝，那么我们
			// 不能假设m（由loadOne添加）
			// 的明确要求足以构建它包含的包。我们必须加载完整的
			// 可传递依赖关系图，以确保我们看到所有相关的
			// 依赖关系。
			if pruning != pruned || summary.pruning == unpruned {
				nextPruning := summary.pruning
				if pruning == unpruned {
					nextPruning = unpruned
				}
				for _, r := range summary.require {
					enqueue(r, nextPruning)
				}
			}
		})
	}

	for _, m := range roots {
		enqueue(m, pruning)
	}
	<-loadQueue.Idle()

	// 在工作区模式下以选定版本重新加载非
	// 的主模块的任何依赖项，因为
	// 需求不能准确反映可传递的导入。
	if pruning == workspace {
		// hasDepsInAll包含需要加载的模块集
		// 在工作区修剪，因为它们的任何依赖项都可能
		// 提供所有包。
		hasDepsInAll := make(map[string]bool)
		seen := map[module.Version]bool{}
		for _, m := range roots {
			hasDepsInAll[m.Path] = true
			seen[m] = true
		}
		// 此循环将终止，因为它将对每个版本的
		// hasDepsInAll中模块的每个依赖项最多调用一次enqueue（并且只有
		// 对每个依赖项的不断增加的版本调用enqueue）。
		for {
			needsEnqueueing := map[module.Version]bool{}
			for p := range hasDepsInAll {
				m := module.Version{Path: p, Version: mg.g.Selected(p)}
				reqs, ok := mg.g.RequiredBy(m)
				if !ok {
					needsEnqueueing[m] = true
					continue
				}
				for _, r := range reqs {
					s := module.Version{Path: r.Path, Version: mg.g.Selected(r.Path)}
					if cmpVersion(s.Version, r.Version) > 0 && !seen[s] {
						needsEnqueueing[s] = true
					}
				}
			}
			// 将所有需要排队添加到我们关心的路径
			if len(needsEnqueueing) == 0 {
				break
			}

			for p := range needsEnqueueing {
				enqueue(p, workspace)
				seen[p] = true
				hasDepsInAll[p.Path] = true
			}
			<-loadQueue.Idle()
		}
	}

	if hasError {
		return mg, mg.findError()
	}
	return mg, nil
}

// RequiredBy返回图中模块m所需的依赖项，
// 如果删除模块m的依赖项，则ok=false。
// 
// 调用者不能修改返回的切片，但可以安全地附加到它上面，并且可以依赖它不被修改。
func (mg *ModuleGraph) RequiredBy(m module.Version) (reqs []module.Version, ok bool) {
	return mg.g.RequiredBy(m)
}

// Selected返回具有给定路径的模块的选定版本。
// 
// 如果未选择版本，则所选版本返回“无”。
func (mg *ModuleGraph) Selected(path string) (version string) {
	return mg.g.Selected(path)
}

// WalkBreadthFirst为每个模块调用f一次，以宽度优先顺序，
// version，而不是图中显示的“none”，无论是否选择了该版本。
func (mg *ModuleGraph) WalkBreadthFirst(f func(m module.Version)) {
	mg.g.WalkBreadthFirst(f)
}

// BuildList返回图形中所有模块的选定版本，
// 从目标开始。
// 
// 列表中剩余元素的顺序是确定性的，但是任意的。
// 
// 调用者不能修改返回的列表，但可以安全地附加到列表中
// 并且可以依赖它不被修改。
func (mg *ModuleGraph) BuildList() []module.Version {
	mg.buildListOnce.Do(func() {
		mg.buildList = capVersionSlice(mg.g.BuildList())
	})
	return mg.buildList
}

func (mg *ModuleGraph) findError() error {
	errStack := mg.g.FindPath(func(m module.Version) bool {
		cached := mg.loadCache.Get(m)
		return cached != nil && cached.(summaryError).err != nil
	})
	if len(errStack) > 0 {
		err := mg.loadCache.Get(errStack[len(errStack)-1]).(summaryError).err
		var noUpgrade func(from, to module.Version) bool
		return mvs.NewBuildListError(err, errStack, noUpgrade)
	}

	return nil
}

func (mg *ModuleGraph) allRootsSelected() bool {
	var roots []module.Version
	if inWorkspaceMode() {
		roots = MainModules.Versions()
	} else {
		roots, _ = mg.g.RequiredBy(MainModules.mustGetSingleMainModule())
	}
	for _, m := range roots {
		if mg.Selected(m.Path) != m.Version {
			return false
		}
	}
	return true
}

// LoadModGraph加载并返回主模块的模块依赖关系图，
// 不加载任何包。
// 
// 如果goVersion字符串为非空，则返回的图形为给定Go版本解释的图形
// 而不是Go.mod文件中指示的版本
// 。
// 
// 模块在LoadPackages中自动（并以惰性方式）加载：
// LoadModGraph只需在LoadPackages不需要时调用，
// 通常在关心模块但不关心特定包的命令中调用。
func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
	rs := LoadModFile(ctx)

	if goVersion != "" {
		pruning := pruningForGoVersion(goVersion)
		if pruning == unpruned && rs.pruning != unpruned {
			// 使用新需求而不是convertDepth，因为convertDepth 
			// 也会更新根目录；在这里，我们要报告未修改的根
			// 尽管它们可能看起来不一致。
			rs = newRequirements(unpruned, rs.rootModules, rs.direct)
		}

		mg, err := rs.Graph(ctx)
		if err != nil {
			base.Fatalf("go: %v", err)
		}
		return mg
	}

	rs, mg, err := expandGraph(ctx, rs)
	if err != nil {
		base.Fatalf("go: %v", err)
	}

	requirements = rs

	return mg
}

// expandGraph从rs加载完整的模块图。
// 
// 如果完整的图显示rs的某个根实际上不是其路径的
// 所选版本，expandGraph将计算一组新的根，这些根
// 是一致的。（对于经过删减的模块图，这可能会导致升级到
// 由于之前删减的需求，其他模块。）
// 
// expandGraph返回更新的根，以及从这些根加载的模块图
// 以及加载该图时遇到的任何错误。
// expandGraph返回非零需求和非零图，无论
// 错误如何。出现错误时，可能无法更新根以保持一致。
func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleGraph, error) {
	mg, mgErr := rs.Graph(ctx)
	if mgErr != nil {
		// 如果没有该图，我们无法更新根：我们不知道将选择哪个版本的可传递依赖项。
		return rs, mg, mgErr
	}

	if !mg.allRootsSelected() {
		// rs的根与图的其余部分不一致。更新
		// 它们。在一个未运行的模块中，这对整个构建列表来说是不可操作的——
		// 它只是将以前可传递的需求提升为
		// 根——但在一个被删减的模块中，它可能会引入以前不相关的
		// 可传递依赖项。

		newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false)
		if rsErr != nil {
			// 更新根目录失败，可能是因为更新所需的可传递
			// 依赖项出错。返回原始要求
			// 代替。
			return rs, mg, rsErr
		}
		rs = newRS
		mg, mgErr = rs.Graph(ctx)
	}

	return rs, mg, mgErr
}

// EditBuildList编辑全局生成列表，首先将add 
// 中的每个模块添加到现有生成列表中，然后调整版本（并根据需要添加或删除
// 需求），直到在
// 给定版本中选择mustSelect中的每个模块。
// 
// （请注意，新添加的模块可能不会在生成的
// 构建列表中被选中：它们可能低于现有需求，或者与mustSelect中的
// 版本冲突。）
// 
// 如果mustSelect中列出的版本互不兼容（由于其中一个
// 列出的模块需要另一个模块的更高版本），请编辑BuildList 
// 返回一个*字符，并使生成列表保持其以前的状态。
// 
// 如果成功，EditBuildList将报告生成列表中任何模块
// 的所选版本是否已作为
// 结果更改（可能更改为或从“无”）。
func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (changed bool, err error) {
	rs, changed, err := editRequirements(ctx, LoadModFile(ctx), add, mustSelect)
	if err != nil {
		return false, err
	}
	requirements = rs
	return changed, err
}

// A constraintorr在EditBuildList中描述了不一致的约束
type ConstraintError struct {
	// Conflict列出了mustSelect中每个版本的冲突源
	// A由于
	// mustSelect中其他版本的要求而无法选择的冲突。
	Conflicts []Conflict
}

func (e *ConstraintError) Error() string {
	b := new(strings.Builder)
	b.WriteString("version constraints conflict:")
	for _, c := range e.Conflicts {
		fmt.Fprintf(b, "\n\t%v requires %v, but %v is requested", c.Source, c.Dep, c.Constraint)
	}
	return b.String()
}

// 源文件需要Dep的冲突文档，它与约束冲突。
// （也就是说，Dep的模块路径与Constraint相同，但版本更高。）
type Conflict struct {
	Source     module.Version
	Dep        module.Version
	Constraint module.Version
}

// tidyRoots将根依赖项修剪为
// 在PKG中保留所有包的相同版本并满足
// 图形修剪不变量（如果适用）所需的最低要求。
func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Requirements, error) {
	mainModule := MainModules.mustGetSingleMainModule()
	if rs.pruning == unpruned {
		return tidyUnprunedRoots(ctx, mainModule, rs.direct, pkgs)
	}
	return tidyPrunedRoots(ctx, mainModule, rs.direct, pkgs)
}

func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
	switch rs.pruning {
	case unpruned:
		return updateUnprunedRoots(ctx, direct, rs, add)
	case pruned:
		return updatePrunedRoots(ctx, direct, rs, pkgs, add, rootsImported)
	case workspace:
		return updateWorkspaceRoots(ctx, rs, add)
	default:
		panic(fmt.Sprintf("unsupported pruning mode: %v", rs.pruning))
	}
}

func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Version) (*Requirements, error) {
	if len(add) != 0 {
		// add在工作区模式下应该为空，因为工作区模式意味着
		// -mod=readonly，这反过来又意味着没有新的需求。导致add为非空的代码路径
		// 在到达这一点之前返回一个错误：要添加的模块集来自
		// resolveMissingImports，后者通过调用
		// queryImport来解析每个包。但是queryImport显式地检查-mod=readonly，并且
		// 返回一个错误。
		panic("add is not empty")
	}
	return rs, nil
}

// tidyPrunedRoots返回一组最小的根需求，这些根需求维护go的
// 不变量。mod file需要为给定的
// 包支持图形修剪：
// 
// 1。对于每个标记为pkgInAll的包，提供
// 包的模块路径作为根包含在内。
// 2。对于所有软件包，提供该软件包的模块要么保持在同一版本中选择的
// 状态，要么由
// 根目录的依赖项升级。
// 
// 如果提供包的任何模块已升级到其先前的
// 版本之上，则调用方可能需要重新加载并重新计算包图。
// 
// 为了确保加载过程最终收敛，调用方应该从整洁的根集中添加任何所需的根（不移除现有的不整洁的根），直到根集合收敛。
func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
	var (
		roots        []module.Version
		pathIncluded = map[string]bool{mainModule.Path: true}
	)
	// 我们首先为“all”中的每个包添加根。
	// 
	// 完成后，我们可能仍需要添加更多根，以覆盖升级版或
	// 否则在“all”中缺少包的测试依赖项。对于那些测试
	// 依赖项，我们更倾向于首先为具有较短导入
	// 堆栈的包添加根，其理论是，这些包的模块需求将
	// 倾向于填充其可传递导入（具有
	// 较深导入堆栈）的需求。因此，我们每次在
	// 中添加一个深度的缺少的依赖项，从“all”中的实际包开始，向外扩展
	// 直到扫描了加载的每个包。
	var (
		queue  []*loadPkg
		queued = map[*loadPkg]bool{}
	)
	for _, pkg := range pkgs {
		if !pkg.flags.has(pkgInAll) {
			continue
		}
		if pkg.fromExternalModule() && !pathIncluded[pkg.mod.Path] {
			roots = append(roots, pkg.mod)
			pathIncluded[pkg.mod.Path] = true
		}
		queue = append(queue, pkg)
		queued[pkg] = true
	}
	module.Sort(roots)
	tidy := newRequirements(pruned, roots, direct)

	for len(queue) > 0 {
		roots = tidy.rootModules
		mg, err := tidy.Graph(ctx)
		if err != nil {
			return nil, err
		}

		prevQueue := queue
		queue = nil
		for _, pkg := range prevQueue {
			m := pkg.mod
			if m.Path == "" {
				continue
			}
			for _, dep := range pkg.imports {
				if !queued[dep] {
					queue = append(queue, dep)
					queued[dep] = true
				}
			}
			if pkg.test != nil && !queued[pkg.test] {
				queue = append(queue, pkg.test)
				queued[pkg.test] = true
			}
			if !pathIncluded[m.Path] {
				if s := mg.Selected(m.Path); cmpVersion(s, m.Version) < 0 {
					roots = append(roots, m)
				}
				pathIncluded[m.Path] = true
			}
		}

		if len(roots) > len(tidy.rootModules) {
			module.Sort(roots)
			tidy = newRequirements(pruned, roots, tidy.direct)
		}
	}

	_, err := tidy.Graph(ctx)
	if err != nil {
		return nil, err
	}
	return tidy, nil
}

// UpdateProuneDroots返回一组根需求，用于维护go的
// 不变量。需要支持图形修剪的mod文件：
// 
// 1。提供标有
// pkgInAll或pkgIsRoot的每个包的模块的选定版本包含在根目录中。
// 请注意，某些根模式（例如“…”）可能会分解根集
// 以包含提供任何其他模块导入（或仅
// 所需）的任何包的每个模块。
// 2。每个根仅出现一次，在其路径
// （如果rs.graph为非nil）的选定版本处，或在最高版本处，否则显示为
// （否则）。
// 3。在rs中显示为根的每个模块路径仍然是根。
// 4。除非
// /（add中现有根或另一个模块的依赖项）升级，否则add中的每个版本均按其给定版本进行选择。
// 
// 假定PKG中的包是从
// rs的根或rs图中选择的模块加载的。
// 
// 上述不变量一起意味着
// 开始。修改文件：
// 
// 1。（导入不变量。）通过主模块中的任何包或测试导入的可传递提供包
// 的每个模块都包含为根。
// 接下来是上文（1）和（3）的归纳。传递导入的
// 在调用过程中加载的包被标记为pkgInAll（1），
// 并且假设在之前的
// 调用中加载的任何传递导入的包都已经是rs（3）的根。
// 
// 2。（参数不变。）提供与
// /匹配的包的每个模块都包含一个显式包模式作为根。这直接遵循
// （1）：匹配显式包模式的包被标记为
// pkgIsRoot。
// 
// 3。（完备性不变。）主模块或其中一个模块
// 的每个模块。这个不变量由调用者决定，调用者必须
// 明确要求为构建提供任何包
// 不能从模块图外部加载包，但可以向
// 图添加根，但由（3）简化。如果调用方将根添加到
// 中的图中以解决丢失的包，那么UpdatePronedRoots将保留它们，
// 这些根的选定版本不能回归，它们将
// 最终写回主模块的go。mod文件。
// 
// （参见https:
// 详情。）
func updatePrunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
	roots := rs.rootModules
	rootsUpgraded := false

	spotCheckRoot := map[module.Version]bool{}

	// “提供每个标有
	// pkgInAll或PKGISOOT的软件包的模块的选定版本包含在根目录中。”
	needSort := false
	for _, pkg := range pkgs {
		if !pkg.fromExternalModule() {
			// pkg不是从模块依赖项加载的，所以我们不需要
			// 来执行任何特殊操作来维护该依赖项。
			continue
		}

		switch {
		case pkg.flags.has(pkgInAll):
			// pkg是通过主模块中的包或测试传递导入的。huo jian defg
			// 我们所依赖的所有文件。
			// 
			// （这是使图形修剪成为可能的“导入不变量”）

		case rootsImported && pkg.flags.has(pkgFromRoot):
			// pkg是某个根的可传递依赖项，我们将
			// 根视为由主模块导入（如“go-get”中）。huo jian defg

		case pkg.flags.has(pkgIsRoot):
			// 它与命令行参数匹配。）我们希望将来调用
			// /'go'命令（例如在同一个包上进行'go test'）继续使用我们现在使用的相同版本的依赖项。
			// 所以我们需要将这个包的依赖项放入经过修剪的
			// 模块图中。
			// 
			// 使包含此包的模块成为模块图的根
			// 正是这样做的：如果包含此包的模块支持图形
			// 剪枝，那么它应该满足导入不变量本身，因此所有
			// 其依赖项都应该在其范围内。mod file，如果包含包的模块
			// 不支持修剪，那么如果我们将其设置为
			// 根，我们将把其所有（未运行的）可传递依赖项加载到
			// 模块图中。
			// 
			// （这是“参数不变量”，对
			// 再现性很重要。）

		default:
			// pkg是主模块之外的其他包的依赖项。
			// 据我们所知，它与主模块无关（因此也与主模块的消费者无关），它的依赖关系
			// 应该已经包含在模块图中——包含在导入它的包的
			// 的依赖关系中。
			continue
		}

		if _, ok := rs.rootSelected(pkg.mod.Path); ok {
			// 主模块可能已经启动。mod文件不完整或
			// 否则是错误的-例如，作者可能忘记了“git 
			// 添加”他们更新的go。mod file在添加一个新的包导入后，或者
			// 可能他们进行了编辑。mod文件使用第三方工具
			// /（'git merge'？）这并不能保持模块
			// 依赖项的一致性。如果出现这种情况，理想情况下，我们希望检测缺失的
			// 需求，并在这里修复它们。
			// 
			// 然而，我们也需要小心，不要太咄咄逼人。对于
			// 外部测试的可传递依赖项。包含测试本身的
			// 模块的mod文件预计将提供所有
			// 相关依赖项，我们明确不希望引入
			// 关于碰巧发生在
			// go中的*无关*需求的需求。这些可传递的仅测试依赖项的mod文件。（具体示例请参见mod_lazy_test_horizon.txt中的test 
			// “金发区”似乎是在抽查我们提升为显式根的完全相同的
			// 模块：即，提供由主模块传递导入的
			// 包的模块，以及提供包导入图根的模块。这将捕获对主模块go的错误
			// 编辑。mod file和
			// 依赖项中不一致的要求，这些依赖项提供导入的包，但会忽略错误的
			// 或依赖项中误导性的要求，这些要求与主模块中的包明显不相关。
			spotCheckRoot[pkg.mod] = true
		} else {
			roots = append(roots, pkg.mod)
			rootsUpgraded = true
			// 根切片最初是排序的，因为rs.rootModules已排序，
			// 但是我们刚才添加的根可能是无序的。
			needSort = true
		}
	}

	for _, m := range add {
		if v, ok := rs.rootSelected(m.Path); !ok || cmpVersion(v, m.Version) < 0 {
			roots = append(roots, m)
			rootsUpgraded = true
			needSort = true
		}
	}
	if needSort {
		module.Sort(roots)
	}

	// “每个根目录在其路径的选定版本中只出现一次……”
	for {
		var mg *ModuleGraph
		if rootsUpgraded {
			// 我们已经添加或升级了一个或多个根目录，因此加载完整的模块
			// 图表，以便我们可以更新这些根目录，使其与其他
			// 要求一致。
			if mustHaveCompleteRequirements() {
				// 我们对根目录的更改可能已经将依赖项移入或移出
				// 图形修剪视界，这反过来可能会改变所选
				// 其他模块的版本。（对于删减的模块，添加或删除
				// 显式根是一种语义变化，而不仅仅是一种装饰性变化。）
				return rs, errGoModDirty
			}

			rs = newRequirements(pruned, roots, direct)
			var err error
			mg, err = rs.Graph(ctx)
			if err != nil {
				return rs, err
			}
		} else {
			// 由于所有根目录均未升级，我们没有理由怀疑它们与任何其他
			// 根目录的要求不一致。如果我们已经加载了完整的模块图，请只查看它；
			// 否则，只需抽查我们加载包的
			// 中根的明确要求。
			if rs.graph.Load() != nil {
				// 我们已经加载了完整的模块图，其中包括所有根模块的
				// 需求，即使是可传递的
				// 需求，如果它们没有运行的话！
				mg, _ = rs.Graph(ctx)
			} else if cfg.BuildMod == "vendor" {
				// 我们无法抽查其他模块的需求，因为我们
				// 通常没有他们的权限。mod文件可在供应商
				// 目录中找到。（幸运的是，这种情况是不可能的，因为mg.graph是
				// 在供应商模式下总是非零！）
				panic("internal error: rs.graph is unexpectedly nil with -mod=vendor")
			} else if !spotCheckRoots(ctx, rs, spotCheckRoot) {
				// 我们抽查了与我们加载的包相关的根目录的明确要求。不幸的是，它们在某种程度上是不一致的；我们需要加载完整的模块图
				// 以便正确地修复根。
				var err error
				mg, err = rs.Graph(ctx)
				if err != nil {
					return rs, err
				}
			}
		}

		roots = make([]module.Version, 0, len(rs.rootModules))
		rootsUpgraded = false
		inRootPaths := make(map[string]bool, len(rs.rootModules)+1)
		for _, mm := range MainModules.Versions() {
			inRootPaths[mm.Path] = true
		}
		for _, m := range rs.rootModules {
			if inRootPaths[m.Path] {
				// 此根指定一个冗余路径。当我们之前看到它时，我们已经保留了这个路径的
				// 所选版本，所以忽略
				// 冗余副本，不管它的版本如何。
				// 
				// 当我们阅读完整的模块图时，我们包括了
				// 每个根的依赖项，即使该根是冗余的。如果，比如说，一些自动化工具添加了一个冗余的
				// 的可再现性，因为旧版本的要求都是
				// “require”行，然后运行“go mod tidy”，试图使所有
				// 保持一致，那么这将更好地保持
				// 。
				// 
				// 因此，省略以前存在的根可能会*减少*非根的选定版本，但仅删除要求
				// 因此无法*增加*其他根的选定版本-
				// 我们不需要将此更改标记为升级。（此特定的
				// 更改不能使任何其他根无效。）
				continue
			}

			var v string
			if mg == nil {
				v, _ = rs.rootSelected(m.Path)
			} else {
				v = mg.Selected(m.Path)
			}
			roots = append(roots, module.Version{Path: m.Path, Version: v})
			inRootPaths[m.Path] = true
			if v != m.Version {
				rootsUpgraded = true
			}
		}
		// 请注意，rs.rootModules已经按模块路径和版本进行了排序，
		// 我们以相同的顺序附加到根切片，并保证
		// 每个路径只有一个版本，因此根也按模块路径
		// 和（一般）版本排序。

		if !rootsUpgraded {
			if cfg.BuildMod != "mod" {
				// 对根集（如果有）的唯一更改是删除重复项。
				// 这些需求是一致的（如果可能是多余的），所以保留
				// 原始rs以保留其模块图。
				return rs, nil
			}
			// 根集合已收敛：进入此迭代的每个根都已处于其选定版本，尽管我们已删除了同一路径的其他
			// （冗余）根。
			break
		}
	}

	if rs.pruning == pruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
		// 根集不变，rs已被修剪，因此将rs保留为
		// 保留其缓存的ModuleGraph（如果有）。
		return rs, nil
	}
	return newRequirements(pruned, roots, direct), nil
}

// spotCheckRoots报告rs中根的版本是否满足
// mods中模块的明确要求。
func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Version]bool) bool {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	work := par.NewQueue(runtime.GOMAXPROCS(0))
	for m := range mods {
		m := m
		work.Add(func() {
			if ctx.Err() != nil {
				return
			}

			summary, err := goModSummary(m)
			if err != nil {
				cancel()
				return
			}

			for _, r := range summary.require {
				if v, ok := rs.rootSelected(r.Path); ok && cmpVersion(v, r.Version) < 0 {
					cancel()
					return
				}
			}
		})
	}
	<-work.Idle()

	if ctx.Err() != nil {
		// 要么我们抽查不合格，要么打电话的人不再关心我们的
		// 无论如何回答。
		return false
	}

	return true
}

// tidyUnprunedRoots返回维护
// 每个模块的所选版本的最小根需求集，这些模块提供了或在词汇上可能有
// 在pkgs中提供了一个包，并以根目录的形式直接包含每个这样的
// 模块的所选版本。
func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
	var (
		// keep是一组模块，提供程序包，或者是消除导入歧义所需的程序包。
		keep     []module.Version
		keptPath = map[string]bool{}

		// rootpath是直接提供从主模块导入的包的模块路径列表。它们应该被列为根。
		rootPaths   []string
		inRootPaths = map[string]bool{}

		// altMods是一组模块路径，从词汇上讲，这些模块可以提供导入的包。从
		// 显式需求列表中删除这些需求是可以的，如果这会将它们从模块图中删除。如果它们
		// 出现在可从根路径访问的模块图中，则它们不得处于较低版本。这可能会导致缺失和错误或新的
		// 导入歧义。
		// 
		// 例如，假设开发人员重写了示例中的导入。com/m到
		// 示例。com/m/v2，然后运行“go mod tidy”。Tidy可能会删除
		// 示例中的要求。com/m，如果没有其他可传递要求
		// 。然而，如果你举个例子。com/m被降级为
		// go以外的版本。总结，当包的例子。com/m/v2/p加载后，我们将得到一个错误
		// 试图消除导入的歧义，因为我们无法检查示例。com/m 
		// 不含其总和。见#47738。
		altMods = map[string]string{}
	)
	for _, pkg := range pkgs {
		if !pkg.fromExternalModule() {
			continue
		}
		if m := pkg.mod; !keptPath[m.Path] {
			keep = append(keep, m)
			keptPath[m.Path] = true
			if direct[m.Path] && !inRootPaths[m.Path] {
				rootPaths = append(rootPaths, m.Path)
				inRootPaths[m.Path] = true
			}
		}
		for _, m := range pkg.altMods {
			altMods[m.Path] = m.Version
		}
	}

	// 构造一个具有最小根集的构建列表。
	// 这可能会删除或降级altMods中的模块。
	reqs := &mvsReqs{roots: keep}
	min, err := mvs.Req(mainModule, rootPaths, reqs)
	if err != nil {
		return nil, err
	}
	buildList, err := mvs.BuildList([]module.Version{mainModule}, reqs)
	if err != nil {
		return nil, err
	}

	// 检查altMods中的模块是否降级但未移除。
	// 如果是，将它们添加到根中，这将保留一个“
	// 在go.mod中。请参阅上面对altMods的评论。
	keptAltMod := false
	for _, m := range buildList {
		if v, ok := altMods[m.Path]; ok && semver.Compare(m.Version, v) < 0 {
			keep = append(keep, module.Version{Path: m.Path, Version: v})
			keptAltMod = true
		}
	}
	if keptAltMod {
		// 我们必须再次运行mvs.Req，而不是简单地将altMods添加到min中。
		// altMods中的一个需求可能会使其他一些
		// 显式间接需求变得不必要。
		reqs.roots = keep
		min, err = mvs.Req(mainModule, rootPaths, reqs)
		if err != nil {
			return nil, err
		}
	}

	return newRequirements(unpruned, min, direct), nil
}

// UpdateUnpredroots返回一组根需求，其中包括选择的
// 每个模块路径的版本直接作为根，并维护在rs图中选择的
// 每个模块的版本。
// 
// 根被更新为：
// 
// 1。direct中每个模块路径的选定版本都包含在根
// /（如果不是“无”）。
// 2。每个根都是其路径的选定版本。（我们说这样一个根
// 集是“一致的”。）
// 3。rs图中选择的每个版本都将保持选中状态，除非add中的依赖项升级了
// 版本。
// 4。除非由
// /（add中现有根或另一个模块的依赖项）升级，否则add中的每个版本均按其给定版本进行选择。
func updateUnprunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) {
	mg, err := rs.Graph(ctx)
	if err != nil {
		// 我们不能忽略模块图中的错误，即使用户通过-e 
		// 标志试图通过它们。如果我们不能加载完整的模块
		// 依赖项，那么我们就不能可靠地计算它们的最小子集。
		return rs, err
	}

	if mustHaveCompleteRequirements() {
		// 不需要实际更新需求，只需检查是否需要更新
		// 即可。
		if rs == nil {
			// 我们被要求从头开始重新构建需求，但我们甚至不能修改它们。
			return rs, errGoModDirty
		}
		for _, m := range rs.rootModules {
			if m.Version != mg.Selected(m.Path) {
				// 根版本v有误导性：实际选择的版本更高。
				return rs, errGoModDirty
			}
		}
		for _, m := range add {
			if m.Version != mg.Selected(m.Path) {
				return rs, errGoModDirty
			}
		}
		for mPath := range direct {
			if _, ok := rs.rootSelected(mPath); !ok {
				// 模块m应该明确列出，但不是。
				// 
				// 请注意，在包加载过程中也会检测到这种情况（并记录更多的
				// 详细信息），因此在这一点上实际上不应该是
				// 可能——这只是一种深度防御。
				return rs, errGoModDirty
			}
		}

		// 没有显式根丢失，所有根都已在版本
		// 我们希望保留。我们所做的任何其他改变都是纯粹的装饰性的，比如删减多余的间接依赖项。每一期#34822，我们
		// 当我们无法更新go时，忽略表面上的更改。mod文件。
		return rs, nil
	}

	var (
		rootPaths   []string // 如果所选的根目录版本与go中已列出的相同，则应包含为根目录的模块路径
		inRootPaths = map[string]bool{}
	)
	for _, root := range rs.rootModules {
		// 如果所选的根目录版本与go中已列出的相同。mod file，将其作为根目录保留（即使是多余的）以保存到
		// 避免不必要的搅动。（请参阅https:
		// 
		// 即使是间接需求，我们也会这样做，因为我们不知道为什么会添加它们
		// 并且它们可以随时变为直接。
		if !inRootPaths[root.Path] && mg.Selected(root.Path) == root.Version {
			rootPaths = append(rootPaths, root.Path)
			inRootPaths[root.Path] = true
		}
	}

	// “direct中每个模块路径的选定版本都包含在根目录中。”
	// 
	// 这只是为了方便最终用户和清晰起见：在未运行的模块中，
	// 显式依赖与隐式依赖的选择对MVS 
	// 选择（对于其本身或任何其他模块）没有影响。
	keep := append(mg.BuildList()[MainModules.Len():], add...)
	for _, m := range keep {
		if direct[m.Path] && !inRootPaths[m.Path] {
			rootPaths = append(rootPaths, m.Path)
			inRootPaths[m.Path] = true
		}
	}

	var roots []module.Version
	for _, mainModule := range MainModules.Versions() {
		min, err := mvs.Req(mainModule, rootPaths, &mvsReqs{roots: keep})
		if err != nil {
			return rs, err
		}
		roots = append(roots, min...)
	}
	if MainModules.Len() > 1 {
		module.Sort(roots)
	}
	if rs.pruning == unpruned && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
		// 根集未更改，rs已取消运行，因此保留rs到
		// 保留其缓存的ModuleGraph（如果有）。
		return rs, nil
	}

	return newRequirements(unpruned, roots, direct), nil
}

// ConvertPrunning返回具有给定修剪行为的rs版本。
// 如果rs已经具有给定的修剪，则ConvertPrunning返回未修改的rs。
func convertPruning(ctx context.Context, rs *Requirements, pruning modPruning) (*Requirements, error) {
	if rs.pruning == pruning {
		return rs, nil
	} else if rs.pruning == workspace || pruning == workspace {
		panic("attempthing to convert to/from workspace pruning and another pruning type")
	}

	if pruning == unpruned {
		// 我们正在将修剪过的模块转换为未运行的模块。
		// ppruned模块图的根是未运行模块图的根的超集，因此
		// 我们不需要添加任何新根，只需要删除那些
		// 是多余的，这正是UpdateUnpronedroots所做的。
		return updateUnprunedRoots(ctx, rs.direct, rs, nil)
	}

	// 我们正在将一个未运行的模块转换为一个已修剪的模块。
	// 
	// 未运行的模块图包含构建列表中每个
	// 模块的可传递依赖项。事实证明，我们可以将其表示为经过修剪的
	// 根集！“包含构建列表中每个模块的可传递依赖项
	// 列表”正是我们将构建列表中的每个模块
	// 提升为根目录时，删减的模块会发生的情况。
	mg, err := rs.Graph(ctx)
	if err != nil {
		return rs, err
	}
	return newRequirements(pruned, mg.BuildList()[MainModules.Len():], rs.direct), nil
}
